import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import dotenv from 'dotenv'
require('axios-debug-log/enable')

dotenv.config()

// 从环境变量中获取主机地址，如果未设置则使用 "localhost"
const host = process.env.SMARTOBS_HOST || 'localhost'
const javaInstance = createInstance(process.env.API_URL || `http://${host}:28082/javaapi`)
export const goInstance = createInstance(process.env.GO_API_URL || `http://${host}:28082/api`)

type AuditCallback = (
  data1: any,
  data2: any
) => {
  data1: any
  data2: any
}

type AuditConfig = {
  status?: number
  callback?: AuditCallback
}

export async function audit(requestConfig: AxiosRequestConfig<any>, config?: AuditConfig) {
  const status = config?.status || 200
  const callback = config?.callback || ((data1, data2) => ({ data1, data2 }))

  const req1 = await javaInstance.request(requestConfig)
  const req2 = await goInstance.request(requestConfig)

  expect(req1.status).toBe(status)
  expect(req2.status).toEqual(req1.status)

  let data1 = getData(req1)
  let data2 = getData(req2)
  const { data1: expected, data2: actual } = callback(data1, data2)
  expect(actual).toEqual(expected)

  if (req1.config.duration) {
    const improved = req1.config.duration - (req2.config.duration || 0)
    const improvedPercent = (improved / req1.config.duration) * 100

    if (improvedPercent >= 20) {
      console.log(`Go improved by: ${improved}ms`)
    }

    if (improvedPercent <= -20) {
      console.warn(`Go improved by: ${improved}ms`)
    }
  }

  return {
    request1: req1,
    request2: req2
  }
}

function getData(request: AxiosResponse<any, any>) {
  const body = request.data
  const keys = Object.keys(body)
  for (const key of keys) {
    if (key !== 'code' && key !== 'message') {
      return body[key]
    }
  }
  return body.data
}

export function createInstance(baseURL: string) {
  const api = axios.create({
    baseURL,
    headers: {
      'Content-Type': 'application/json'
    },
    paramsSerializer: (params) => {
      return Object.entries(params)
        .map(([key, value]) => {
          if (Array.isArray(value)) {
            return value.map((v) => `${key}=${v}`).join('&')
          } else {
            return `${key}=${value}`
          }
        })
        .join('&')
    }
  })

  api.interceptors.request.use(
    (config) => {
      config.startTime = new Date().getTime()

      return config
    },
    (error) => {
      return Promise.reject(error)
    }
  )

  api.interceptors.response.use(
    (response) => {
      if (response.config.startTime) {
        const end = new Date().getTime()

        const duration = end - response.config.startTime

        response.config.duration = duration
      }

      return response
    },
    (error) => {
      return Promise.reject(error)
    }
  )

  return api
}
